package com.chiancloud.shield.core.conf;


import com.chiancloud.shield.core.exception.BadConfigException;
import com.chiancloud.shield.tools.ShieldUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MapOperations implements Map<String, String> {
  private static final Logger log =
    LoggerFactory.getLogger(MapOperations.class);


  public final Map<String, String> options;

  public final String name;

  public MapOperations() {
    options = new HashMap<>();
    name = "";
  }

  public MapOperations(String name, Map<String, String> options) {
    assert options != null : "null map";
    this.options = options;
    this.name = name;
  }


  public String getOption(String key, String defVal) {
    String val = options.get(key);
    return val != null ? val : defVal;
  }



  public String getMandatoryOption(String key) throws BadConfigException {
    String val = options.get(key);
    if (val == null) {
      if (log.isDebugEnabled()) {
        log.debug("Missing key {} from config containing {}",
                  key, this);
      }
      throw new BadConfigException("Missing option " + key);
    }
    return val;
  }


  public int getOptionInt(String option, int defVal) {
    String val = getOption(option, Integer.toString(defVal));
    return Integer.decode(val);
  }

  public int getMandatoryOptionInt(String option) throws BadConfigException {
    getMandatoryOption(option);
    return getOptionInt(option, 0);
  }


  public void verifyOptionSet(String key) throws BadConfigException {
    if (ShieldUtils.isUnset(getOption(key, null))) {
      throw new BadConfigException("Unset option %s", key);
    }
  }
  
  public void mergeWithoutOverwrite(Map<String, String> that) {
      ShieldUtils.mergeMapsIgnoreDuplicateKeys(options, that);
  }


  public void mergeMapPrefixedKeys(Map<String, String> that,
                                    String prefix,
                                    boolean overwrite) {
    for (Entry<String, String> entry : that.entrySet()) {
      String key = entry.getKey();
      if (key.startsWith(prefix)) {
        if (overwrite || get(key) == null) {
          put(key, entry.getValue());
        }
      }
    }
  }


  public void putIfUnset(String key, String value) {
    if (get(key) == null) {
      put(key, value);
    }
  }

  public void set(String key, Object value) {
    assert value != null;
    put(key, value.toString());
  }

  public int size() {
    return options.size();
  }

  public boolean isEmpty() {
    return options.isEmpty();
  }

  public boolean containsValue(Object value) {
    return options.containsValue(value);
  }

  public boolean containsKey(Object key) {
    return options.containsKey(key);
  }

  public String get(Object key) {
    return options.get(key);
  }

  public String put(String key, String value) {
    return options.put(key, value);
  }

  public String remove(Object key) {
    return options.remove(key);
  }

  public void putAll(Map<? extends String, ? extends String> m) {
    options.putAll(m);
  }

  public void clear() {
    options.clear();
  }

  public Set<String> keySet() {
    return options.keySet();
  }

  public Collection<String> values() {
    return options.values();
  }

  public Set<Entry<String, String>> entrySet() {
    return options.entrySet();
  }

  @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
  public boolean equals(Object o) {
    return options.equals(o);
  }

  @Override
  public int hashCode() {
    return options.hashCode();
  }

  public boolean isSet(String key) {
    return ShieldUtils.isSet(get(key));
  }

  @Override
  public String toString() {
    StringBuilder builder = new StringBuilder();
    builder.append(name).append("=\n");

    for (Entry<String, String> entry : options.entrySet()) {
      builder.append("  ")
             .append(entry.getKey())
             .append('=')
             .append(entry.getValue())
             .append('\n');
    }
    return builder.toString();
  }
}
